/**
 *
 * AST walker for AMFM AST that emits java code to generate music files from source AMFM code.
 * The java code uses modified version of Fractal Composer as the underlying library to help
 * with self similarity manipulations and midi/sound files generation.
 *
 * @author talgiat
 */
tree grammar AmFmCodeGenerator;
options {
 tokenVocab=AmFm;
 ASTLabelType=CommonTree;
 output=template;
}
@header{
/*
 * Copyright 2009, Team Mondegreen:
 * Brian Hsieh - bh2181@columbia.edu
 * Eugene Yao - eyy2102@columbia.edu
 * Jake Amonette - jaa2149@columbia.edu
 * Tal Giat - tg2235@colubia.edu
 * 
 * This file is part of AMFM (A Minimal Fractal Music Language), a language created for
 * fractal music as part of the Programming Languages and Translator class at Columbia university.
 *
 * For more details go to: http://code.google.com/p/amfm/
 * 
 * AMFM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * at your option any later version.
 * 
 * AMFM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with AMFM.  If not, see <http://www.gnu.org/licenses/>. 
 */

package org.mondegreen.amfm.compiler;

import java.util.Map;
}
@members{
private static final int DEFAULT_OCTAVE = 4;
private Map<String,String> instrumentsMap;

public void setInstrumentsMap(Map<String,String> instrumentsMap) {
  this.instrumentsMap = instrumentsMap;
}

//private String buildWithComma(StringTemplate st
}

// AMFM program 
program[String filePath,String progamName]: ^(PROGRAM (s+=seed)+ (stf=start_func)) 
                            -> amfm_class_tpl(file_path={filePath},class_name={progamName},seeds={$s}, start_function={$stf.st});

// Seeds rules
seed  : ^(ID INSTRUMENT (nu+=note_unit)+) 
        -> seed_tpl(name={$ID.text},nu={$nu}, 
                    instrument={instrumentsMap != null && instrumentsMap.get($INSTRUMENT.text) != null ? instrumentsMap.get($INSTRUMENT.text) : "NO_INSTRIMENT"}); 
note_unit : ^(NOTEUNIT ^(NOTE n=note) (d=duration (v=volume)?)?) 
            -> {%{"" + $n.st + (d != null ? "," + $d.st : "") + (v != null ? "," + $v.st : "") }} 
          | ^(NOTEUNIT REST (d=duration)?) 
            -> {%{$REST.text + (d != null ? "," + $d.st : "")}}
          ;
duration: FRACTION -> {%{$text}} | 
          number -> {%{$text}} 
          ;
volume  : VOLUME_LEVELS_MINUS_F -> {%{$text}}
        | 'F' -> {%{$text}}
        ;
note :  nl=note_letter o=octave? -> {%{"" + $nl.st + (o != null ? $o.st : DEFAULT_OCTAVE)}}; 
note_letter: NOTELETTERS_WITH_ACCIDENTAL_MINUS_F  -> {%{$NOTELETTERS_WITH_ACCIDENTAL_MINUS_F.text}}
           | 'F'ACCIDENTAL? -> {%{"F" + ($ACCIDENTAL.text != null ? $ACCIDENTAL.text : "")}}
           ;
octave: ONE_TO_NINE -> {%{$text}};

// Start function
start_func:  ^(START (f+=function)*)
             -> start_function_tpl(functions={$f});
function: p=play -> function_call_tpl(all_args={$p.st})
        | ss_f=ss_func -> function_call_tpl(all_args={$ss_f.st})
        ; 
play: ^(PLAY fp=func_params) -> {%{$fp.st + ", false, false, false, false"}};
ss_func: ^(fn=ss_func_name sfp=ss_func_params) -> {%{$sfp.st + ", " + $fn.st}};
ss_func_name: SS_PITCH -> {%{"true, false, false"}}
            | SS_VOLUME -> {%{"false, false, true"}}
            | SS_RHYTHM -> {%{"false, true, false"}}
            | SS_PITCH_VOLUME -> {%{"true, false, true"}}
            | SS_PITCH_RHYTHM -> {%{"true, true, false"}}
            | SS_RHYTHM_VOLUME -> {%{"false, true, true"}} 
            | SS_ALL -> {%{"true, true, true"}}
            ;
ss_func_params: fp=func_params BOOLEAN -> {%{"" + $fp.st + ", " + ("TRUE".equals($BOOLEAN.text) ? "true" : "false" ) }};
func_params: ID sb=start_bar nb=num_of_bars -> {%{"\"" + $ID.text +  "\", " + $sb.text + ", " + $nb.text}}; 
start_bar: number ;
num_of_bars: number ;
number  : ONE_TO_NINE | TEN_AND_UP;